///\todo upgrade

//////////////////////////////////////////////////////////////////////////////////
//                                                                              //
//  This file is part of the buola project (https://code.google.com/p/buola/).  //
//                                                                              //
//  Copyright(c) 2007-2012 Xavi Gratal                                          //
//  gratal AT gmail DOT com                                                     //
//                                                                              //
//  Buola is free software: you can redistribute it and/or modify               //
//  it under the terms of the GNU General Public License as published by        //
//  the Free Software Foundation, either version 3 of the License, or           //
//  (at your option) any later version.                                         //
//                                                                              //
//  Buola is distributed in the hope that it will be useful,                    //
//  but WITHOUT ANY WARRANTY; without even the implied warranty of              //
//  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the               //
//  GNU General Public License for more details.                                //
//                                                                              //
//  You should have received a copy of the GNU General Public License           //
//  along with buola.  If not, see <http://www.gnu.org/licenses/>.              //
//                                                                              //
//////////////////////////////////////////////////////////////////////////////////

#ifndef _BUOLA_CV_COCTREEDEFAULTNODE_H_
#define _BUOLA_CV_COCTREEDEFAULTNODE_H_

#include <buola/mat.h>

namespace buola { namespace cvi {

template<typename tType>
class COcTreeDefaultNode
{
    enum EState
    {
        STATE_EMPTY,
        STATE_PARENT,
        STATE_DATA
    };
    
public:
    COcTreeDefaultNode()
        :   mState(STATE_EMPTY)
    {}

    COcTreeDefaultNode(const tType &pValue)
        :   mState(STATE_DATA)
    {
        new(&mValue) tType(pValue);
    }

    COcTreeDefaultNode(COcTreeDefaultNode &&pO)
        :   mState(pO.mState)
    {
        if(pO.mState==STATE_PARENT)
        {
            mChildren=pO.mChildren;
            pO.mState=STATE_EMPTY;
        }
        else if(pO.mState==STATE_DATA)
        {
            new(&mValue) tType(std::move(pO.mValue));
            pO.mValue.~tType();
            pO.mState=STATE_EMPTY;
        }
    }

    COcTreeDefaultNode(const COcTreeDefaultNode &pO)
        :   mState(pO.mState)
    {
        if(pO.mState==STATE_PARENT)
        {
            mChildren=new COcTreeDefaultNode*[8];
            for(int i=0;i<8;i++)
            {
                if(pO.mChildren[i])
                    mChildren[i]=new COcTreeDefaultNode(*pO.mChildren[i]);
                else
                    mChildren[i]=nullptr;
            }
        }
        else if(pO.mState==STATE_DATA)
        {
            new(&mValue) tType(pO.mValue);
        }
    }

    ~COcTreeDefaultNode()
    {
        if(mState==STATE_PARENT)
        {
            for(int i=0;i<8;i++)
                delete mChildren[i];
            delete[] mChildren;
        }
        else if(mState==STATE_DATA)
        {
            mValue.~tType();
        }
    }

    bool HasData() const            {   return mState==STATE_DATA;      }
    bool HasChildren() const        {   return mState==STATE_PARENT;    }
    bool HasChild(int i) const      {   return mChildren[i];            }

    COcTreeDefaultNode *Child(int i)       {    return mChildren[i];    }
    const COcTreeDefaultNode *Child(int i) const   {   return mChildren[i];    }

    void SetValue(const tType &pValue)
    {
        mState=STATE_DATA;
        mValue=pValue;
    }
    
    const tType &Value() const  {   return mValue;  }
    tType &Value()              {   return mValue;  }

    void Expand()
    {
        tType lValue=mValue;
        
        mState=STATE_PARENT;
        mChildren=new COcTreeDefaultNode*[8];
        
        for(int i=0;i<8;i++)
        {
            mChildren[i]=new COcTreeDefaultNode(lValue);
        }
    }

    COcTreeDefaultNode* CreateChild(int i)
    {
        if(mState!=STATE_PARENT)
        {
            mState=STATE_PARENT;
            mChildren=new COcTreeDefaultNode*[8];
            for(int i=0;i<8;i++)
                mChildren[i]=nullptr;
        }
        mChildren[i]=new COcTreeDefaultNode;
        return mChildren[i];
    }

    COcTreeDefaultNode &operator=(COcTreeDefaultNode &&pRH)
    {
        if(mState==STATE_PARENT)
        {
            for(int i=0;i<8;i++)
                delete mChildren[i];
            delete[] mChildren;
        }
        else if(mState==STATE_DATA)
        {
            mValue.~tType();
        }

        mState=pRH.mState;
        
        if(pRH.mState==STATE_PARENT)
        {
            mChildren=pRH.mChildren;
            pRH.mState=STATE_EMPTY;
        }
        else if(pRH.mState==STATE_DATA)
        {
            new(&mValue) tType(std::move(pRH.mValue));
            pRH.mValue.~tType();
            pRH.mState=STATE_EMPTY;
        }

        return *this;
    }
    
private:
    EState mState;

    union
    {
        COcTreeDefaultNode **mChildren;
        tType mValue;
    };
};

/*namespace cvi*/ } /*namespace buola*/ }

#endif
